package org.softmed.rest.server.defaults.generation;

import groovy.lang.Binding;
import groovy.lang.GroovyClassLoader;
import groovy.lang.GroovyShell;
import groovy.util.GroovyScriptEngine;

import java.io.File;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.softmed.filehandling.FileUtil;
import org.softmed.persistence.PersistenceManagerProvider;
import org.softmed.persistence.PersistenceProvider;
import org.softmed.rest.generation.scafold.AppConfig;
import org.softmed.rest.generation.scafold.ModConfig;
import org.softmed.rest.generation.scafold.build.PersistenceProviderReader;
import com.google.inject.Inject;
import com.google.inject.name.Named;

public class DefaultPersistenceProviderReader implements
		PersistenceProviderReader {

	@Inject
	GroovyScriptEngine groovyScriptEngine;

	@Inject
	GroovyClassLoader groovyClassLoader;

	@Inject
	@Named("AspectProviderName")
	String aspectProviderName;

	@Inject
	@Named("RESTAdminProviderName")
	String restProviderName;

	@Inject
	@Named("DatabaseBackupDirectory")
	String databaseBackupDirectory;

	FileUtil util = new FileUtil();
	BackupBuilder backupBuilder = new BackupBuilder();
	DefaultAdminBuilder defaultAdminBuilder = new DefaultAdminBuilder();

	String providerName = "name";
	String providerVariableName = "provider";
	String fileName = "persistence.groovy";
	String appVariableName = "app";
	String moduleVariableName = "mod";

	@Override
	public void readPersistenceProviders(List<AppConfig> previousApps,
			List<AppConfig> apps, Map<String, String> options) throws Throwable {

		resetAccordingToOptions(previousApps, options);

		for (AppConfig app : apps) {
			try {
				readGenerationConfig(app);
			} catch (Throwable t) {
				t.printStackTrace();
			}
		}
		System.out.println("PersistenceProvider Read");
	}

	private void resetAccordingToOptions(List<AppConfig> previousApps,
			Map<String, String> options) throws Throwable {

		if (options == null) {
			PersistenceManagerProvider.stopAndRefreshClassDefinitions();
			return;
		}

		try {

			String resetOption = options.get("persistence");

			if (resetOption == null || resetOption.length() == 0
					|| resetOption.equals("refresh")) {
				PersistenceManagerProvider.stopAndRefreshClassDefinitions();
			} else if (resetOption.equals("backup")) {
				PersistenceManagerProvider.stopProviders();
				PersistenceManagerProvider.refreshClassDefinitions();
				backupBuilder.backupDatabases(databaseBackupDirectory,
						previousApps);
				PersistenceManagerProvider.clearProviderList();
			} else if (resetOption.equals("delete-all-except-admin")) {

				List<String> providersNamesToDelete = new ArrayList<String>();
				Set<String> providerNames = PersistenceManagerProvider
						.getProviders().keySet();
				providersNamesToDelete.addAll(providerNames);
				providersNamesToDelete.remove(restProviderName);
				providersNamesToDelete.remove(aspectProviderName);

				List<PersistenceProvider> providersToDelete = getProviders(providersNamesToDelete);

				PersistenceManagerProvider.stopProviders();
				PersistenceManagerProvider.refreshClassDefinitions();
				backupBuilder.backupDatabases(databaseBackupDirectory,
						previousApps);
				PersistenceManagerProvider.deleteDatabases(providersToDelete);
				PersistenceManagerProvider.clearProviderList();

			} else if (resetOption.equals("default")) {
				PersistenceManagerProvider.stopProviders();
				PersistenceManagerProvider.refreshClassDefinitions();
				backupBuilder.backupDatabases(databaseBackupDirectory,
						previousApps);
				PersistenceManagerProvider.deleteAllDatabases();
				defaultAdminBuilder.setDefaultAdministrationDatabases();
				PersistenceManagerProvider.clearProviderList();
			} else if (resetOption.equals("delete-all")) {
				PersistenceManagerProvider.stopProviders();
				PersistenceManagerProvider.refreshClassDefinitions();
				backupBuilder.backupDatabases(databaseBackupDirectory,
						previousApps);
				PersistenceManagerProvider.deleteAllDatabases();
				PersistenceManagerProvider.clearProviderList();
			} else { // delete only the one specified in the option
				List<String> providersNamesToDelete = new ArrayList<String>();
				providersNamesToDelete.add(resetOption);
				List<PersistenceProvider> providersToDelete = getProviders(providersNamesToDelete);

				PersistenceManagerProvider.stopProviders();
				PersistenceManagerProvider.refreshClassDefinitions();
				backupBuilder.backupDatabases(databaseBackupDirectory,
						previousApps);
				PersistenceManagerProvider.deleteDatabases(providersToDelete);
				PersistenceManagerProvider.clearProviderList();
			}

		} catch (Throwable t) {
			t.printStackTrace();
			System.err
					.println("ERROR - Trying only to stop and refresh persistence providers");
			PersistenceManagerProvider.stopAndRefreshClassDefinitions();
		}

	}

	protected List<PersistenceProvider> getProviders(
			List<String> providersNamesToDelete) {
		List<PersistenceProvider> providersToDelete = new ArrayList<PersistenceProvider>();
		for (String providerName : providersNamesToDelete) {
			PersistenceProvider provider = PersistenceManagerProvider
					.getProviders().get(providerName);
			providersToDelete.add(provider);
		}
		return providersToDelete;
	}

	private void readGenerationConfig(AppConfig app) throws Throwable {
		File root = new File(app.getPath());
		File[] files = root.listFiles();
		for (File file : files) {
			if (file.isFile() && file.getName().equals(fileName))
				readBaseGenerationConfig(file, app);
		}

		List<ModConfig> mods = app.getModules();
		for (ModConfig mod : mods) {
			readGenerationConfig(app, mod);
		}

	}

	private void readGenerationConfig(AppConfig app, ModConfig mod)
			throws Throwable {
		File root = new File(mod.getPath());
		File[] files = root.listFiles();
		for (File file : files) {
			if (file.isFile() && file.getName().equals(fileName))
				readBaseGenerationConfigFromMod(file, app, mod);
		}
	}

	private void readBaseGenerationConfigFromMod(File file, AppConfig app,
			ModConfig mod) throws Throwable {
		app.setPersistenceDefinitionFileExists(true);
		String script = util.readFromFile(file);

		List<Class> classes = mod.getClasses();
		String importSnippet = "import org.softmed.persistence.*;\n";
		for (Class type : classes) {
			importSnippet += "import " + type.getCanonicalName() + ";\n";
		}

		script = importSnippet + script;

		Binding binding = new Binding();
		binding.setVariable(appVariableName, app);
		binding.setVariable(moduleVariableName, mod);
		GroovyShell shell = new GroovyShell(groovyClassLoader, binding);

		try {
			shell.evaluate(script);
		} catch (Throwable t) {
			System.err.println("Error evaluating script " + file.getName()
					+ " from Module " + mod.getName() + " from Application "
					+ app.getName());
			System.err.println("Groovy Script File Path : "
					+ file.getCanonicalPath());
			t.printStackTrace();
		}

		String name = null;
		PersistenceProvider provider = null;
		try {
			name = (String) binding.getVariable(providerName);
		} catch (Throwable t) {
			// t.printStackTrace();
		}

		try {
			provider = (PersistenceProvider) binding
					.getVariable(providerVariableName);

		} catch (Throwable t) {
			// t.printStackTrace();
		}

		if (provider != null) {
			provider.start();
			PersistenceManagerProvider.registerProvider(name, provider);
		}

		mod.setPersistenceProvider(name);
		// groovyScriptEngine.run(scriptName, binding);

	}

	private void readBaseGenerationConfig(File file, AppConfig app)
			throws Throwable {
		app.setPersistenceDefinitionFileExists(true);
		String script = util.readFromFile(file);

		List<Class> classes = app.getClasses();
		String importSnippet = "import org.softmed.persistence.*;\n";
		for (Class type : classes) {
			importSnippet += "import " + type.getCanonicalName() + ";\n";
		}

		script = importSnippet + script;

		Binding binding = new Binding();
		binding.setVariable(appVariableName, app);
		GroovyShell shell = new GroovyShell(groovyClassLoader, binding);

		try {
			shell.evaluate(script);
		} catch (Throwable t) {
			System.err.println("Error evaluating script " + file.getName()
					+ " from Application " + app.getName());
			System.err.println("Groovy Script File Path : "
					+ file.getCanonicalPath());
			throw t;
		}

		String name = null;
		PersistenceProvider provider = null;
		try {
			name = (String) binding.getVariable(providerName);
		} catch (Throwable t) {
			// t.printStackTrace();
		}

		try {
			provider = (PersistenceProvider) binding
					.getVariable(providerVariableName);

		} catch (Throwable t) {
			// t.printStackTrace();
		}

		if (provider != null) {
			provider.start();
			PersistenceManagerProvider.registerProvider(name, provider);
		}

		app.setPersistenceProvider(name);
	}

}
